#include "opt.h"
#include <cassert>
using namespace Opt_NS;
using std::vector;
using std::string;
using std::map;
//-------------------------------------------------------------------------------
string OptOption_S::toStr()const{ return sValue; }
const char* OptOption_S::toCStr()const{ return sValue.c_str(); }
int OptOption_S::toInt()const{ return atoi(sValue.c_str()); }
float OptOption_S::toFloat()const{ return atof(sValue.c_str()); }
double OptOption_S::toDouble()const{ return atof(sValue.c_str()); }
bool OptOption_S::toBool()const{ return (sValue=="true"); }
//-------------------------------------------------------------------------------
Opt_C::Opt_C(const string &p_sHelp):m_sExeName("EXE"),m_sHelpOption(p_sHelp),m_bParsed(false)
{
    addOption(m_sHelpOption, "display this help and exit");
}
//-------------------------------------------------------------------------------
Opt_C::~Opt_C()
{
    map<string, OptOption_S*>::iterator iter;
    for(iter = m_mapOptions.begin(); iter != m_mapOptions.end(); ++iter){
        delete iter->second;
    }
}
//-------------------------------------------------------------------------------
void Opt_C::parseOption(int argc, char** argv){
    const char* sHelp = m_sHelpOption.c_str();
    m_sExeName = argv[0];
    for(int i=1;i<argc;++i){
        if(!strcmp(argv[i], sHelp)){
            printOption();
            exit(0);
        }
        if(!isValidOption(argv[i]))
            continue;
        OptOption_S* pOpt = m_mapOptions[argv[i]];
        if(pOpt->eType == Opt::text && i+1 < argc){
            setOptionValue(argv[i], argv[i+1]);
        }else if(pOpt->eType == Opt::boolean){
            setOptionValue(argv[i], "true");
        }
    }
    m_bParsed = true;
}
//-------------------------------------------------------------------------------
void Opt_C::addOption(const std::string &p_sName, const std::string &p_sInfo, const std::string &p_sDefault, OptType p_eType)
//void Opt_C::addOption(const char* p_sName, const char* p_sInfo, const char* p_sDefault, OptType p_eType)
{
    map<string, OptOption_S*>::iterator iter = m_mapOptions.find(p_sName);
    OptOption_S *opt;
    if(iter == m_mapOptions.end()){//Not Found
        opt = new OptOption_S;
    }else{
        opt = iter->second;
    }
    opt->sName = p_sName;
    opt->sValue = p_sDefault;
    opt->sDefault = p_sDefault;
    opt->sInfo = p_sInfo;
    opt->eType = p_eType;
    if(p_eType == Opt::boolean){
        opt->sDefault = "false";
    }
    m_mapOptions[p_sName] = opt;
    m_vOptions.push_back(opt);
}
//-------------------------------------------------------------------------------
OptOption_S Opt_C::getOption(const string &p_sName) const
{
    map<string, OptOption_S*>::const_iterator iter = m_mapOptions.find(p_sName);
    if(iter == m_mapOptions.end()){//Not Found
        printf("Option %s not found!\n", p_sName.c_str());
        assert(0);
    }
    return *(iter->second);
}
//-------------------------------------------------------------------------------
bool Opt_C::setOptionValue(const string &p_sName, const string &p_sValue)
{
    if(!isValidOption(p_sName)){
        printf("Option %s not found!\n", p_sName.c_str());
        return false;
    }
    m_mapOptions[p_sName]->sValue = p_sValue;
    return true;
}
//-------------------------------------------------------------------------------
bool Opt_C::isValidOption(const string &p_sName) const
{
    map<string, OptOption_S*>::const_iterator iter = m_mapOptions.find(p_sName);
    if(iter == m_mapOptions.end()){//Not Found
        return false;
    }
    return true;
}
//-------------------------------------------------------------------------------
bool Opt_C::isSet(const string &p_sName)const
{
    map<string, OptOption_S*>::const_iterator iter = m_mapOptions.find(p_sName);
    if(iter == m_mapOptions.end()){//Not Found
        printf("Option %s not found!\n", p_sName.c_str());
        assert(0);
    }
    OptOption_S *pOpt = iter->second;
    if(pOpt->sValue==""){
        return false;
    }
    return true;
}
//-------------------------------------------------------------------------------
bool Opt_C::checkValid()const
{
    map<string, OptOption_S*>::const_iterator iter;
    for(iter = m_mapOptions.begin();iter!=m_mapOptions.end();iter++){
        OptOption_S *pOpt = iter->second;
        if(pOpt->bRequire && pOpt->sValue==""){
            printf("Error: Required Option(%s) Not Set.\n", pOpt->sName.c_str());
            printOption();
            return false;
        }
    }
    return true;
}
//-------------------------------------------------------------------------------
bool Opt_C::setRequireOption(const string &p_sName, bool p_bRequire)
{
    if(!isValidOption(p_sName)){
        printf("[SETREQOPT] Option %s not found!\n", p_sName.c_str());
        return false;
    }
    m_mapOptions[p_sName]->bRequire = p_bRequire;
    return true;
}
//-------------------------------------------------------------------------------
void Opt_C::printOption()const
{
    printf("\nUsage: %s [OPTIONS]\n", m_sExeName.c_str());
    printf("*For Text option, you need a value after it.\n");
    printf("*For Boolean option, no value is needed.\n");
    printf("\nOptions:\n");
    vector<OptOption_S*>::const_iterator iter;
    for(int i=0;i<2;++i){
        printf("\n-------%s Options-------\n", !i?"Text":"Boolean");
        for(iter = m_vOptions.begin();iter!=m_vOptions.end();iter++){
            OptOption_S *pOpt = *iter;
            
            if(pOpt->eType != i)continue;

            printf("\n  %s : %s\n", pOpt->sName.c_str(), pOpt->sInfo.c_str());

            if(pOpt->sDefault!="")
                printf("  (Default Value=%s)\n", pOpt->sDefault.c_str());
        }
    }
}
//-------------------------------------------------------------------------------
void Opt_C::printValue()const
{
    printf("Options:\n");
    map<string, OptOption_S*>::const_iterator iter;
    for(iter = m_mapOptions.begin();iter!=m_mapOptions.end();iter++){
        OptOption_S *pOpt = iter->second;
        if(pOpt->sValue!="")
        printf("  %s = %s\n", pOpt->sName.c_str(), pOpt->sValue.c_str());
    }
}
//-------------------------------------------------------------------------------

